using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace FerrSimplicity
{
    /// <summary>
    /// The core object of FerrSimplicity, it provides a container for as many
    /// components as you wish, as well as a location in 2D and 3D space. It passes
    /// along all the standard XNA events to child components.
    /// </summary>
    public class BaseGameObject : DrawableGameComponent, ICloneable
    {
        #region Fields
        protected List<BaseComponent> Components;
        private   bool                Loaded;
        #endregion

        #region Properties
        /// <summary>
        /// This is the location/orientation of the object in 2D and 3D space. 
        /// </summary>
        public Transform3D Transform { get; set; }
        /// <summary>
        /// The number of components that are currently attached to this object.
        /// </summary>
        public int         ComponentCount
        {
            get { return Components.Count; }
        }
        #endregion

        #region Constructor
        private BaseGameObject()
            : base(null) { }
        /// <summary>
        /// Allocates memory for a few items
        /// </summary>
        /// <param name="aGame">Since this inherits from the DrawableGameComponent, it needs access to the XNA game</param>
        public BaseGameObject(Game aGame)
            : base(aGame)
        {
            Transform  = new Transform3D        ();
            Components = new List<BaseComponent>();
        }
        #endregion

        #region Methods
        protected override void LoadContent  ()
        {
            Loaded = true;
            for (int i = 0; i < Components.Count; i++)
            {
                Components[i].LoadContent();
                if (Enabled)
                    Components[i].Enable(true);
            }
            base.LoadContent();
        }
        protected override void UnloadContent()
        {
            Loaded = false;
            for (int i = 0; i < Components.Count; i++)
            {
                Components[i].UnloadContent();
                if (Enabled)
                    Components[i].Enable(false);
            }
            base.UnloadContent();
        }

        public override void Update(GameTime aGameTime)
        {
            for (int i = 0; i < Components.Count; i++)
            {
                Components[i].Update(aGameTime);
            }
            base.Update(aGameTime);
        }
        public override void Draw  (GameTime aGameTime)
        {
            for (int i = 0; i < Components.Count; i++)
            {
                Components[i].Draw(aGameTime);
            }
            base.Draw(aGameTime);
        }

        protected override void OnEnabledChanged(object sender, EventArgs args)
        {
            for (int i = 0; i < Components.Count; i++)
                Components[i].Enable(Enabled);

            base.OnEnabledChanged(sender, args);
        }

        /// <summary>
        /// Adds anything that inherits BaseComponent to this object's component list, and links 
        /// up the component's parent to this object. It also will call LoadContent on the component
        /// if the object is already loaded, as well as call Enable if the Enabled property is true.
        /// </summary>
        /// <param name="aComponent">Any class that inherits from the BaseComponent class.</param>
        public void AddComponent(BaseComponent aComponent)
        {
            Components.Add(aComponent);
            aComponent.Parent = this;

            if (Loaded)
            {
                aComponent.LoadContent();
                if (Enabled)
                    aComponent.Enable(true);
            }
        }

        /// <summary>
        /// This calls UnloadContent, Enable(false) and removes all child components 
        /// that inherit from type T.
        /// </summary>
        /// <typeparam name="T">The type of component to remove</typeparam>
        public void RemoveComponent<T>() where T : BaseComponent
        {
            for (int i = 0; i < Components.Count; i++)
            {
                if (Components[i] is T)
                {
                    // disables the components
                    if (Loaded)
                    {
                        Components[i].UnloadContent();
                        if (Enabled)
                            Components[i].Enable(false);
                    }

                    Components.RemoveAt(i);
                    i--;
                }
            }
        }
        /// <summary>
        /// This calls UnloadContent, Enable(false) and removes the component, if it's 
        /// attached to this object.
        /// </summary>
        /// <param name="aComponent">The specific instance of the component to remove</param>
        public void RemoveComponent   (BaseComponent aComponent)
        {
            if (Components.Contains(aComponent))
            {
                if (Loaded)
                    aComponent.UnloadContent();
                if (Enabled)
                    aComponent.Enable(false);

                Components.Remove(aComponent);
            }
        }


        /// <summary>
        /// This method returns the first child component in the component list that inherits from type T
        /// </summary>
        /// <typeparam name="T">The type of BaseComponent to find</typeparam>
        /// <returns>First component in the component list that inherits from type T, or null</returns>
        public T             GetComponent<T>() where T : BaseComponent
        {
            for (int i = 0; i < Components.Count; i++)
            {
                if (Components[i] is T)
                    return (T)Components[i];
            }
            return null;
        }
        /// <summary>
        /// Returns the child component at index of aIndex, or null if out of bounds
        /// </summary>
        /// <param name="aIndex">Index of the BaseComponent from the component list to get</param>
        /// <returns>Returns the child component at index of aIndex, or null if out of bounds</returns>
        public BaseComponent GetComponent   (int aIndex)
        {
            if (aIndex >= 0 && aIndex < Components.Count)
                return Components[aIndex];
            else
                return null;
        }

        /// <summary>
        /// This is the prefered way of removing an item from the game, as it
        /// triggers OnEnabledChanged(false) on the way out
        /// </summary>
        public void RemoveSelf()
        {
            Enabled = false;
            Game.Components.Remove(this);
        }
        #endregion

        #region Overrides
        /// <summary>
        /// Creates a string in the format of [+/-]obj:[ComponentCount]
        /// </summary>
        /// <returns>string in the format of [+/-]obj:[ComponentCount]</returns>
        public override string ToString()
        {
            string coms = "";
            for (int i = 0; i < ComponentCount; i++)
                coms += Components[i].GetType().Name + " \n";

            return (Loaded ? "+" : "-") + "obj: " + ComponentCount + " \n" + coms;
        }
        #endregion

        #region ICloneable
        public object Clone()
        {
            BaseGameObject go = new BaseGameObject(Game);
            go.Loaded   = Loaded;
            go.Transform = new Transform3D(Transform);

            for (int i = 0; i < Components.Count; i++)
            {
                go.AddComponent((BaseComponent)Components[i].Clone());
            }

            return go;
        }
        #endregion
    }
}
